﻿/*
    Copyright (c) 2008 - 2009, Carlos Guzmán Álvarez

    All rights reserved.

    Redistribution and use in source and binary forms, with or without modification, 
    are permitted provided that the following conditions are met:

        * Redistributions of source code must retain the above copyright notice, 
          this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright notice, 
          this list of conditions and the following disclaimer in the documentation and/or 
          other materials provided with the distribution.
        * Neither the name of the author nor the names of its contributors may be used to endorse or 
          promote products derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Windows.Threading;
using Framework.ExtensionMethods;
using Framework.Net.Xmpp.Core;
using Framework.Net.Xmpp.InstantMessaging;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;
using Microsoft.Win32;
using XClient.Configuration;
using XClient.Infrastructure;
using XClient.Interfaces;

namespace XClient.ViewModels
{
    public class SessionViewModel : ViewModel<XmppSession>
    {
        #region · Fields ·

        private IUnityContainer                     container;
        private Account                             currentAccount;
        private ObservableCollection<XmppMessage>   receivedMessages;
        private XmppSession                         session;
        private XmppPresenceState                   presenceState;
        private IChatViewManager                    chatViewManager;

        #endregion

        #region · Properties ·

        public override string HeaderInfo
        {
            get { return "Contacts"; }
        }

        public string DisplayName
        {
            get
            {
                if (this.session.State != XmppSessionState.LoggedIn ||
                    this.currentAccount == null)
                {
                    return String.Empty;
                }

                return this.currentAccount.DisplayName;
            }
        }

        public string Presence
        {
            get
            {
                if (this.session.State == XmppSessionState.LoggedIn ||
                    this.currentAccount == null)
                {
                    return String.Empty;
                }

                return this.currentAccount.Presence;
            }
        }

        public XmppPresenceState PresenceState
        {
            get { return this.presenceState; }
            set 
            {
                if (this.presenceState != value)
                {
                    this.container.Resolve<ConfigurationManager>().Configuration.Save();

                    this.presenceState = value;
                    this.currentAccount.Presence = this.presenceState.ToString();
                    
                    if (this.container.Resolve<XmppSession>().State == XmppSessionState.LoggedIn)
                    {
                        this.container.Resolve<XmppSession>().SetPresence(value, this.Status);
                    }
                }
            }
        }

        public string AvatarImage
        {
            get
            {
                if (this.session.State == XmppSessionState.LoggedIn)
                {
                    return this.session.Configuration.GetOnlineAvatarPath(this.session.UserId.BareIdentifier);
                }

                return null;
            }
        }

        public string Status
        {
            get
            {
                if (this.session.State != XmppSessionState.LoggedIn ||
                    this.currentAccount == null)
                {
                    return String.Empty;
                }
                return this.currentAccount.Status;
            }
            set
            {
                if (this.currentAccount != null)
                {
                    if (this.currentAccount.Status != value)
                    {
                        this.container.Resolve<ConfigurationManager>().Configuration.Save();
                        this.currentAccount.Status = value;                        
                        this.container.Resolve<XmppSession>().SetPresence(this.PresenceState, value);
                    }
                }
            }
        }

        public XmppRoster Contacts
        {
            get { return this.container.Resolve<XmppSession>().Roster; }
        }

        public ObservableCollection<XmppMessage> ReceivedMessages
        {
            get
            {
                if (this.receivedMessages == null)
                {
                    this.receivedMessages = new ObservableCollection<XmppMessage>();
                }
                return this.receivedMessages;
            }
        }

        #endregion

        #region · Constructors ·

        public SessionViewModel(
            IUnityContainer container, 
            IRegionManager  regionManager, 
            IChatViewManager viewManager) : base()
        {
            this.container          = container;
            this.session            = this.container.Resolve<XmppSession>();
            this.chatViewManager    = viewManager;

            this.WireEvents();
        }

        #endregion

        #region · Methods ·

        public void SelectAvatarImage()
        {
            OpenFileDialog ofd = new OpenFileDialog();

            ofd.Filter = "Pictures|*.png;*.gif*;*.jpg;*.bmp|All Files|*.*";

            if (ofd.ShowDialog().Value)
            {
                // Publish the avatar
                ThreadPool.QueueUserWorkItem(new WaitCallback(PublishAvatar), ofd.FileName);
            }
        }

        #endregion

        #region · Private Methods ·

        private void WireEvents()
        {
            this.session.StateChanged               += new EventHandler<XmppSessionStateChangedEventArgs>(OnSessionStateChanged);
            this.session.NormalMessageReceived      += new EventHandler<XmppMessageReceivedEventArgs>(OnMessageReceived);
            this.session.HeadlineMessageReceived    += new EventHandler<XmppMessageReceivedEventArgs>(OnMessageReceived);
        }

        private void PublishAvatar(object state)
        {
            string          avatarFilePath  = (string)state;
            Image           selectedImage   = Image.FromFile(avatarFilePath);
            Image           originalAvatar  = null;
            Image           onlineAvatar    = null;
            MemoryStream    stream          = new MemoryStream();

            try
            {
                // Avatar images
                originalAvatar = ImageHelper.Resize(selectedImage, new Size(96, 96));
                onlineAvatar = ImageHelper.Resize(selectedImage, new Size(32, 32));

                originalAvatar.Save(stream, ImageFormat.Png);

                // Calculate the avatar hash
                string hash = stream.ToArray().ComputeSHA1Hash().ToHexString();

                // Publish the avatar image
                this.session.PublishAvatar("image/png", hash, originalAvatar, onlineAvatar);

                this.Dispatcher.BeginInvoke(
                    DispatcherPriority.ApplicationIdle,
                    new ThreadStart(delegate
                    {
                        // Update the configuration
                        this.currentAccount.Avatar = hash;

                        // Save configuration
                        this.container.Resolve<ConfigurationManager>().Configuration.Save();

                        // Notify avatar change
                        this.NotifyPropertyChanged("AvatarImage");
                    }));
            }
            catch
            {
#warning TODO: Handle Exception !!
                throw;
            }
            finally
            {
                if (selectedImage != null)
                {
                    selectedImage.Dispose();
                }

                if (originalAvatar != null)
                {
                    originalAvatar.Dispose();
                    originalAvatar = null;
                }

                if (onlineAvatar != null)
                {
                    onlineAvatar.Dispose();
                    onlineAvatar = null;
                }

                if (stream != null)
                {
                    stream.Dispose();
                    stream = null;
                }
            }
        }

        #endregion

        #region · Event Handlers ·

        private void OnSessionStateChanged(object sender, XmppSessionStateChangedEventArgs e)
        {
            if (e.NewState == XmppSessionState.LoggedOut)
            {
                this.ReceivedMessages.Clear();
            }
            else if (e.NewState == XmppSessionState.LoggedIn)
            {
                this.currentAccount = this.container.Resolve<ConfigurationManager>().SelectedAccount;

                this.PresenceState = (XmppPresenceState)Enum.Parse(typeof(XmppPresenceState), this.currentAccount.Presence);
            }

            this.NotifyPropertyChanged("DisplayName");
            this.NotifyPropertyChanged("Presence");
            this.NotifyPropertyChanged("AvatarImage");
            this.NotifyPropertyChanged("Status");
            this.NotifyPropertyChanged("Contacts");
            this.NotifyPropertyChanged("PresenceState");
        }

        private void OnMessageReceived(object sender, XmppMessageReceivedEventArgs e)
        {
            this.ReceivedMessages.Add(e.Message);
        }

        #endregion
    }
}
